﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Windows.Forms.DataVisualization.Charting;
using LeakView.common;
using log4net;

namespace LeakView.service
{
    public enum MiscType
    {
        DeltaNOW, Delta10M, Delta30M, Delta60M,
        HeadLL, HeadLS, HeadYL, HeadWD, Head15M, /*Head30M, Head01H, Head02H,*/
        TailLL, TailLS, TailYL, TailWD, Tail15M, /*Tail30M, Tail01H, Tail02H,*/
    }

    public class MiscTypeUtil
    {
        public static string ToUnit(MiscType type)
        {
            switch(type)
            {
                case MiscType.HeadLS:
                case MiscType.TailLS:
                case MiscType.DeltaNOW:
                    return "m³/h";
                
                case MiscType.Delta10M:
                case MiscType.Delta30M:
                case MiscType.Delta60M:
                case MiscType.HeadLL:
                case MiscType.TailLL:
                case MiscType.Head15M:
                case MiscType.Tail15M:
                    return "m³";

                case MiscType.HeadYL:
                case MiscType.TailYL:
                    return "Mpa";

                case MiscType.TailWD:
                case MiscType.HeadWD:
                    return "℃";

                default:
                    return "";

            }
        } 
    }

    public class MiscModal : TimerModule
    {
        private static readonly ILog log = LogManager.GetLogger(typeof(MiscModal));

        public static readonly MiscModal me = new MiscModal();

        string urlHome;

        private long validSeconds = 10;
        public long ValidMilliseconds { get => validSeconds * 1000; }

        private string headName;
        public string HeadName { get => headName; }
        private string tailName;
        public string TailName { get => tailName; }

        readonly Dictionary<MiscType, string> keyMap = new Dictionary<MiscType, string>();
        readonly Dictionary<string, double> valueMap = new Dictionary<string, double>();

        public override void OnModuleInitialize()
        {
            base.OnModuleInitialize();

            this.Period = 2000;

            AnyConfig config = AnyConfigs.get("MISC");

            this.headName = config.get("HeadName").GetOrDefault("起点");
            this.tailName = config.get("TailName").GetOrDefault("终点");

            this.validSeconds = config.get("ValidSeconds").GetOrDefault(10);

#if DEBUG
            this.urlHome = config.get("URL_DEBUG").GetOrDefault("http://127.0.0.1:8080/misc/");
#else
            this.urlHome = config.get("URL").GetOrDefault("http://127.0.0.1:8080/misc/");
#endif

            foreach (MiscType type in Enum.GetValues(typeof(MiscType)))
            {
                string name = Enum.GetName(typeof(MiscType), type);

                string key = config.get(name).GetOrDefault("");

                keyMap.Add(type, key);

            }

        }

        private void Fetch()
        {
            StringBuilder sb = new StringBuilder();

            foreach(string key in keyMap.Values)
            {
                if(key.Length > 0)
                    sb.Append(key).Append(',');
            }

            string keys = sb.ToString();

            using (Ajax ajax = new Ajax())
            {
                AjaxContent content = AjaxContent.Get()
                    .setUrl(urlHome, "value.do")
                    .AddParameter("keys", keys);


                var result = ajax.DoPost<Dictionary<string, AjaxMiscValue>>(content);
                if (result != null)
                {
                    foreach (KeyValuePair<string, AjaxMiscValue> kvp in result)
                    {
                        valueMap[kvp.Key] = GetDoubleFromMiscValue(kvp.Value);
                    }
                }
                else
                {
                    foreach (string key in keyMap.Values)
                    {
                        //if (key.Length > 0)
                            valueMap[key] = double.NaN;
                    }
                }
            }

        }

        protected override void OnTimer()
        {
            Fetch();
        }

        public double GetValue(MiscType type)
        {
            try
            {
                string key = keyMap[type];
                if (valueMap.ContainsKey(key))
                {
                    return valueMap[key];
                }
                else
                {
                    return double.NaN;
                }
                
            }
            catch
            {
                return double.NaN;
            }
        }

        public List<string> LoadFileList(MiscType type)
        {

            using (Ajax ajax = new Ajax())
            {
                AjaxContent content = AjaxContent.Get()
                    .setUrl(urlHome, "list.do")
                    .AddParameter("key", keyMap[type]);

                List<string> result = ajax.DoPost<List<string>>(content);
                if (result != null)
                {
                    return result;
                }
                else
                {
                    return null;
                }
            }

        }

        public List<AjaxMiscValue> LoadHistoryToday(MiscType type)
        {
            if (!keyMap.ContainsKey(type))
                return null;

            string key = keyMap[type];

            using (Ajax ajax = new Ajax())
            {
                AjaxContent content = AjaxContent.Get()
                    .setUrl(urlHome, "historyToday.do")
                    .AddParameter("key", key);

                return ajax.DoPost<List<AjaxMiscValue>>(content);
            }
        }

        public List<AjaxMiscValue> LoadHistory(MiscType type, string filename)
        {
            using (Ajax ajax = new Ajax())
            {
                AjaxContent content = AjaxContent.Get()
                    .setUrl(urlHome, "history.do")
                    .AddParameter("key", keyMap[type])
                    .AddParameter("filename", filename);

                return ajax.DoPost<List<AjaxMiscValue>>(content);
            }

        }


        public List<AjaxMiscValue> LoadHistory(MiscType type, int year, int month, int day)
        {
            using (Ajax ajax = new Ajax())
            {
                AjaxContent content = AjaxContent.Get()
                    .setUrl(urlHome, "historyByDay.do")
                    .AddParameter("key", keyMap[type])
                    .AddParameter("year", year.ToString())
                    .AddParameter("month", month.ToString())
                    .AddParameter("day", day.ToString());

                return ajax.DoPost<List<AjaxMiscValue>>(content);
            }

        }

        public string GetFetcherValueParameter(string valueKey, string parameterKey)
        {
            using (Ajax ajax = new Ajax())
            {
                AjaxContent content = AjaxContent.Get()
                    .setUrl(urlHome, "parameter.do")
                    .AddParameter("valueKey", valueKey)
                    .AddParameter("parameterKey", parameterKey);

                var result = ajax.DoPost<AjaxAnyResult>(content);

                return result.data.ToString();
            }
        }

        public bool SetFetcherValueParameter(string valueKey, string parameterKey, string parameterValue)
        {
            using (Ajax ajax = new Ajax())
            {
                AjaxContent content = AjaxContent.Get()
                    .setUrl(urlHome, "set_parameter.do")
                    .AddParameter("valueKey", valueKey)
                    .AddParameter("parameterKey", parameterKey)
                    .AddParameter("parameterValue", parameterValue);

                var result = ajax.DoPost<AjaxAnyResult>(content);

                if (result.success)
                {
                    log.Info("Leak confirmed.");
                }
                else
                {
                    log.Warn(result.message);
                }

                return result.success;
            }
        }

        public double GetDoubleFromMiscValue(AjaxMiscValue value)
        {
            if (value == null)
                return double.NaN;

            long age = value.timestamp - value.birth;
            if (age > this.ValidMilliseconds)
            {
                return double.NaN;
            }
            else
            {
                return value.value;
            }
        }

    }

    public class AjaxMiscValue
    {
        public long timestamp { get; set; }
        public long birth { get; set; }
        public double value { get; set; }
    }

    public class MiscValueDataSource : ChartDataSource
    {
        private const int MaxTryCount = 2;
        private readonly MiscType miscType;
        private bool ready = false;
        private int trycount = 0;
        private bool loading = false;

        //private TimeSpan maxTimeSpan = TimeSpan.FromMinutes(10);

        public MiscValueDataSource(MiscType type)
        {
            this.miscType = type;
        }

        public bool Ready { get => ready; }

        public void Initailize(Chart chart, int seriesIndex)
        {
            if (ready || loading) return;

            Thread thread = new Thread(() => {

                try
                {
                    loading = true;

                    var data = MiscModal.me.LoadHistoryToday(miscType);
                    if (data != null)
                    {
                        this.Clear();

                        data.ForEach(t =>
                        {
                            this.Add(Tools.ConvertLongToDateTime(t.timestamp), MiscModal.me.GetDoubleFromMiscValue(t));
                        });

                        if(chart != null)
                        {
                            this.Bind(chart, seriesIndex);
                        }

                        ready = true;
                    }

                }
                catch
                {
                    trycount++;
                    if (trycount == MaxTryCount)
                    {
                        ready = true;
                    }

                }
                finally
                {
                    loading = false;
                }

            });
            thread.Start();
        }

    }
}
